import logging
from rest_framework.response import Response
from rest_framework.pagination import BasePagination
from rest_framework.generics import ListAPIView
from elasticsearch_dsl import Q as ESQ
from .document.course import CourseDocument

logger = logging.getLogger('django')


class SearchCoursePage(BasePagination):
    """ES搜索分页器"""

    # DRF分页器所需的属性
    display_page_controls = False

    def __init__(self):
        self.page_size = 12
        self.max_page_size = 50
        self.count = None
        self.request = None
        self._results = None
        self._next = None
        self._search_history = []  # 存储搜索历史的search_after值
        self._current_page = 1  # 当前页码

    def get_results(self, data):
        """获取结果数据"""
        if not data:
            return []
        if isinstance(data, dict) and 'data' in data:
            return data['data'].get('results', [])
        if isinstance(data, (list, tuple)):
            return data
        return []

    def get_next_link(self):
        """获取下一页链接"""
        if not self._next or not self._results:  # 如果没有下一页或没有结果，返回None
            return None

        # 如果结果数量小于页面大小，说明没有下一页
        try:
            page_size = int(self.request.query_params.get('page_size', self.page_size))
        except ValueError:
            page_size = self.page_size

        if len(self._results) < page_size:
            return None

        url = self.request.build_absolute_uri()
        params = self.request.query_params.copy()

        # 更新页码
        params['page'] = str(self._current_page + 1)

        # 重建URL
        url = url.split('?')[0] + '?' + params.urlencode()
        return url

    def get_previous_link(self):
        """获取上一页链接"""
        if self._current_page <= 1:
            return None

        url = self.request.build_absolute_uri()
        params = self.request.query_params.copy()

        # 更新页码
        params['page'] = str(self._current_page - 1)

        # 重建URL
        url = url.split('?')[0] + '?' + params.urlencode()
        return url

    def has_next(self):
        """是否有下一页"""
        return bool(self._next)

    def has_previous(self):
        """是否有上一页"""
        return False  # search_after模式不支持上一页

    def paginate_queryset(self, search_query, request):
        """处理分页"""
        self.request = request

        # 获取分页参数
        try:
            page_size = int(request.query_params.get('page_size', self.page_size))
        except ValueError:
            page_size = self.page_size

        # 限制每页大小
        if page_size > self.max_page_size:
            page_size = self.max_page_size

        # 获取页码
        try:
            page = int(request.query_params.get('page', '1'))
            if page < 1:
                page = 1
        except ValueError:
            page = 1

        self._current_page = page

        # 添加排序
        search_query = search_query.sort({
            'pub_date': {'order': 'desc'},
            'id': {'order': 'desc'}  # 使用我们自己的id字段而不是_id
        })

        # 设置size
        search_query = search_query.extra(size=page_size)

        # 如果不是第一页，需要使用search_after
        if page > 1:
            # 计算需要跳过的页数
            steps = page - len(self._search_history) - 1

            if steps > 0:
                # 需要向前获取数据以构建历史记录
                temp_query = search_query
                for _ in range(steps):
                    if self._search_history:
                        last_value = self._search_history[-1]
                        temp_query = temp_query.extra(search_after=last_value.split(','))
                    temp_response = temp_query.execute()
                    if not temp_response.hits:
                        break
                    last_hit = temp_response.hits[-1]
                    sort_values = last_hit.meta.sort
                    if sort_values and len(sort_values) >= 2:
                        pub_date_value = int(sort_values[0]) if sort_values[0] is not None else 0
                        search_after_value = f"{pub_date_value},{int(sort_values[1])}"
                        self._search_history.append(search_after_value)

            # 使用历史记录中的search_after值
            if len(self._search_history) >= page - 1:
                search_after = self._search_history[page - 2].split(',')
                search_after = [int(x) for x in search_after]
                search_query = search_query.extra(search_after=search_after)

        return search_query

    def get_paginated_response(self, response):
        """处理分页响应"""
        hits = response.hits
        results = []

        for hit in hits:
            course_dict = hit.to_dict()
            # 添加序列化需要的额外字段
            course_dict['teacher'] = {
                'id': hit.teacher_id,
                'name': hit.teacher_name,
                'title': hit.teacher_title
            }

            # 处理高亮结果
            if hasattr(hit.meta, 'highlight'):
                highlight = {}
                if hasattr(hit.meta.highlight, 'name'):
                    highlight['name'] = hit.meta.highlight.name[0]
                if hasattr(hit.meta.highlight, 'brief'):
                    highlight['brief'] = hit.meta.highlight.brief[0]
                if hasattr(hit.meta.highlight, 'teacher_name'):
                    highlight['teacher_name'] = hit.meta.highlight.teacher_name[0]
                course_dict['highlight'] = highlight

            results.append(course_dict)

        # 保存结果，这个要在获取next_link之前设置
        self._results = results

        # 获取下一页的search_after值
        if hits:
            last_hit = hits[-1]
            try:
                # 获取排序值
                sort_values = last_hit.meta.sort
                if sort_values and len(sort_values) >= 2:
                    # 第一个值是时间戳（可能是None），第二个是ID
                    pub_date_value = int(sort_values[0]) if sort_values[0] is not None else 0
                    self._next = f"{pub_date_value},{int(sort_values[1])}"
                else:
                    logger.warning(f"未能获取到完整的排序值: {sort_values}")
                    self._next = None
            except Exception as e:
                logger.error(f"处理排序值时出错: {str(e)}")
                self._next = None
        else:
            self._next = None

        # 保存总数
        self.count = response.hits.total.value

        # 获取分页链接
        next_link = self.get_next_link()
        previous_link = self.get_previous_link()

        # 如果总数小于等于已经获取的数量，next应该为None
        try:
            page_size = int(self.request.query_params.get('page_size', self.page_size))
            if self.count <= len(results) or len(results) < page_size:
                next_link = None
        except ValueError:
            pass

        return Response({
            "code": 1,
            "message": "查询成功",
            "data": {
                'total': self.count,
                'next': next_link,
                'previous': previous_link,
                'results': results,
            }
        })


class CourseSearchAPIView(ListAPIView):
    """课程搜索API"""
    pagination_class = SearchCoursePage

    def get_queryset(self):
        """
        此方法仅用于DRF的接口文档生成
        """
        return []

    def list(self, request, *args, **kwargs):
        """
        重写list方法以适配ES搜索
        """
        try:
            # 获取查询参数
            nav_name = request.query_params.get("nav_name", "")
            category_id = request.query_params.get("categoryId", "")
            sort_ord = request.query_params.get("sortord", "")
            price_range = request.query_params.get("price_range", "")
            keywords = request.query_params.get("keywords", "")

            logger.info(f"搜索参数: nav_name={nav_name}, category_id={category_id}, "
                        f"sort_ord={sort_ord}, price_range={price_range}, keywords={keywords}")

            # 构建ES查询
            s = CourseDocument.search()

            # 基础过滤：状态为上线
            s = s.filter('term', status=1)

            # 导航分类过滤
            if nav_name:
                s = s.filter('term', nav_category_names=nav_name)

            # 课程分类过滤
            if category_id and category_id != '0':
                s = s.filter('term', category_ids=int(category_id))

            # 价格类型过滤
            if price_range:
                if price_range == '1':  # 免费
                    s = s.filter('term', course_type=1)
                elif price_range == '2':  # 付费
                    s = s.filter('term', course_type=2)

            # 关键词搜索
            if keywords:
                # 1. 首先尝试精确匹配
                exact_query = ESQ(
                    'multi_match',
                    query=keywords,
                    fields=['name^3', 'brief^2', 'teacher_name^2', 'tag_names'],
                    type='best_fields',
                    minimum_should_match='90%'
                )

                # 2. 相关内容匹配
                related_query = ESQ(
                    'multi_match',
                    query=keywords,
                    fields=['name', 'brief', 'teacher_name', 'tag_names'],
                    type='most_fields',
                    minimum_should_match='10%'
                )

                # 3. 组合查询
                s = s.query(
                    ESQ('bool',
                        should=[exact_query, related_query],
                        minimum_should_match=1
                        )
                )

                # 添加高亮配置
                s = s.highlight_options(
                    pre_tags=['<em class="highlight">'],  # 高亮开始标签
                    post_tags=['</em>'],  # 高亮结束标签
                    number_of_fragments=0,  # 返回完整字段而不是片段
                    type='unified'  # 使用unified高亮器，提供更好的高亮效果
                )
                s = s.highlight('name', 'brief', 'teacher_name')

            # 如果没有任何过滤条件，返回所有结果
            if not any([nav_name, category_id, price_range, keywords]):
                s = s.query('match_all')

            # 应用分页
            paginator = self.pagination_class()
            s = paginator.paginate_queryset(s, request)

            # 执行查询
            logger.info(f"ES查询DSL: {s.to_dict()}")
            response = s.execute()

            # 返回分页响应
            return paginator.get_paginated_response(response)

        except Exception as e:
            logger.error(f"搜索API异常: {str(e)}")
            return Response({
                "code": 0,
                "message": "搜索失败",
                "data": {
                    "total": 0,
                    "next": None,
                    "previous": None,
                    "results": []
                }
            }, status=500)